/** @file    SharedMemory.cpp
 *  @author  Alessandro Polo
 *  @version $Id: SharedMemory.cpp 2873 2010-08-08 19:06:41Z alex $
 *  @brief
 * File containing methods for the wosh::SharedMemory class.
 * The header for this class can be found in SharedMemory.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/SharedMemory.h>

 #include <core/ObjectAllocator.h>
 #include <core/BusManager.h>
 #include <core/SecurityManager.h>
 #include <core/WoshKernel.h>
 #include <core/WoshHost.h>
 #include <core/BusRing.h>
 #include <core/MessageProcessingCommon.h>
 #include <core/MessageFilter.h>
 #include <core/Request.h>
 #include <core/Response.h>
 #include <core/Table.h>
 #include <core/DataModels.h>
 #include <core/ThreadMethod.h>
 #include <math.h>


using namespace std;
namespace wosh {


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::SharedMemoryInfo, "", 1.01, _static_SharedMemoryInfo)

SharedMemoryInfo& SharedMemoryInfo::operator=(const SharedMemoryInfo& m) {
	if (this == &m) return *this; // same object?
	this->id = m.id;
	this->kernel = m.kernel;
	this->seniority = m.seniority;
	this->clock = m.clock;
	this->primary = m.primary;
	this->mode = m.mode;
	this->strategy = m.strategy;
	this->propagation = m.propagation;
	this->lastSeen = m.lastSeen;
//	this->died = m.died;
	return *this;
 }

bool SharedMemoryInfo::operator==(const SharedMemoryInfo& other) const {
	if ( this->id != other.id ) return false;
	return true;
 }

std::string SharedMemoryInfo::toString() const {
	return Utilities::format("SharedMemoryInfo#%ld", this->id);
 }

bool SharedMemoryInfo::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	ret = hash_context->update( this->id ) & ret;
	ret = hash_context->update( this->kernel ) & ret;
	ret = hash_context->update( this->seniority ) & ret;
	ret = hash_context->update( this->clock ) & ret;
	ret = hash_context->update( this->primary ) & ret;
	ret = hash_context->update( this->mode ) & ret;
	ret = hash_context->update( this->strategy ) & ret;
	ret = hash_context->update( this->propagation ) & ret;
	ret = hash_context->update( this->lastSeen ) & ret;
//	ret = hash_context->update( this->died ) & ret;
	return ret;
 }

DataFieldCollector<SharedMemoryInfo>* SharedMemoryInfo::getDataFields() {
	DataFieldCollector<SharedMemoryInfo>* fields = new DataFieldCollector<SharedMemoryInfo>();
	fields->add<long,Variant>(&SharedMemoryInfo::getID, "ID", 0);
	fields->add<const std::string&,Variant>(&SharedMemoryInfo::getKernel, "Kernel", 1);
	fields->add<double,Variant>(&SharedMemoryInfo::getSeniority, "Seniority", 2);
	fields->add<int64,Variant>(&SharedMemoryInfo::getClock, "Clock", 3);
	fields->add<short,Variant>(&SharedMemoryInfo::getMode, "Mode", 4);
	fields->add<short,Variant>(&SharedMemoryInfo::getStrategy, "Strategy", 5);
	fields->add<short,Variant>(&SharedMemoryInfo::getPropagation, "Propagation", 6);
	fields->add<long,Variant_DT>(&SharedMemoryInfo::getLastSeen, "LastSeen", 7);
	fields->add<const char*,Variant>(&SharedMemoryInfo::getClassName, "Type", 8, false);
	return fields;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Meyer singleton! static reference is allocated on first user, destroyed by compiler
// not thread safe
extern "C" {
	ListT<SharedMemory*>& SharedMemory::getInstances() {
		static ListT<SharedMemory*> instances;
		return instances;
	}
}

 WOSH_REGISTER_INTERFACE(wosh::SharedMemory, "wosh::Object", 1.05, _static_SharedMemory)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

SharedMemory::SharedMemory( const std::string& name, Object* parent )
	: Object(parent) {

	this->methodPeriodicElection = NULL;
	this->methodPeriodicDataSynch = NULL;

	this->info.id = Utilities::randomLong();
	this->info.id = _GET_Abs(this->info.id);
	this->info.kernel = WoshKernel::getKernelName();
	this->info.primary = true;
	this->info.mode = (short)SharedMemory::MODE_ADHOC;
	this->info.strategy = (short)SharedMemory::STRATEGY_UNKNOWN;
	this->info.propagation = (short)SharedMemory::PROPAGATION_INVALIDATE;
	this->info.seniority = Utilities::std_time();
	this->info.seniority += (double)_GET_Abs(Utilities::randomInt()) / (double)LIMIT_UINT_MAX;

	Log.setIndent( 3 );
	Log.setContext( "SharedMemory" );
	setName(name);
	Log.setLevel( LOG_INFO );
Log.setLevel( LOG_DEBUG );

	// initialize agent list with myself
	// (basically..assume I'm the (only one) primary and resolve conflicts later on election response,
	// this should be fine since myself and my data-set are definitely out-of-date respect to another living agent,
	// moreover I easily know I was in this use-case when eventually receiving election reply)
	this->agents.set( this->info.id, &this->info);

	Log(LOG_DEBUG, " Initializing Properties.." );
	Properties.set( _SharedMemory_KEY_ID,					this->info.id, Permission(Permission::Read) );
	Properties.set( _SharedMemory_KEY_Seniority,			this->info.seniority, Permission(Permission::Read) );
	Properties.set( _SharedMemory_KEY_Clock,				this->info.clock, Permission(Permission::Read) ); // synch read on request
	Properties.set( _SharedMemory_KEY_Primary,				this->info.primary, Permission(Permission::Read) ); // synch read on request
	Properties.set( _SharedMemory_KEY_Mode,					this->info.mode, Permission(Permission::Read) );
	Properties.set( _SharedMemory_KEY_Strategy,				this->info.strategy, Permission(Permission::Read) );
	Properties.set( _SharedMemory_KEY_Propagation,			this->info.propagation, Permission::RW_R__R__() );
	Properties.set( _SharedMemory_KEY_StatsAgentsCount,		1, Permission(Permission::Read) ); // synch read on request
	Properties.set( _SharedMemory_KEY_StatsDataSetCount,	0, Permission(Permission::Read) ); // synch read on request
	Properties.set( _SharedMemory_KEY_PeriodAutoElection,	Variant_TS(0), Permission::RW_R__R__() );
	Properties.set( _SharedMemory_KEY_PeriodAutoDataSynch,	Variant_TS(0), Permission::RW_R__R__() );
	Properties.updateObjectKeys(this);
	Properties.setPropertiesProviderListener(this);

	Log(LOG_DEBUG, " Registering Methods.." );
	MethodPropertyT::createMethods( &Methods, &Properties, &Log );
	MethodChT::createMethods( &Methods, this, &Properties, &Log );

	MethodList* mmAgentList = Methods.create<MethodList>( _SharedMemory_METHOD_get_agents, "Get Agents", Permission::R_XR_XR__() );
	mmAgentList->setDataFieldExtractor( DataFieldExtractor<SharedMemoryInfo>::createFor< DataAdapterMapT<long,SharedMemoryInfo> >(this->agents, "Agents") );

	MethodRetrieveObject<SharedMemoryInfo>* mmAgentInfo = Methods.create< MethodRetrieveObject<SharedMemoryInfo> >( _SharedMemory_METHOD_get_info, "Get Agent-Info", Permission::R_XR_XR__() );
	mmAgentInfo->configure(&this->info, this->agents.getMutex(), &this->getPermission());

	MethodVoid* mmRaiseElection = Methods.create<MethodVoid>( _SharedMemory_METHOD_election, "Raise Election", Permission::R_XR_XR__() );
	mmRaiseElection->setMethod(this, (MethodVoidPtr)&SharedMemory::mmDoElectionRequest);

	SharedMemory::getInstances().transactionBeginWrite();
	SharedMemory::getInstances().push_back(this);
	SharedMemory::getInstances().transactionEnd();
 }

SharedMemory::~SharedMemory() {
	Log(LOG_VERBOSE, ":~SharedMemory() : Destroying.." );

	if ( this->methodPeriodicElection != NULL ) {
		this->methodPeriodicElection->quitThread();
		delete this->methodPeriodicElection; this->methodPeriodicElection = NULL;
	 }
	if ( this->methodPeriodicDataSynch != NULL ) {
		this->methodPeriodicDataSynch->quitThread();
		delete this->methodPeriodicDataSynch; this->methodPeriodicDataSynch = NULL;
	 }

	SharedMemory::getInstances().transactionBeginWrite();
	SharedMemory::getInstances().pop(this);
	SharedMemory::getInstances().transactionEnd();

	this->agents.erase( this->info.id, false ); // it's a shadow reference: don't 'delete'
	this->agents.free();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// FLOW/CONTROL

WRESULT SharedMemory::init( unsigned long timeout ) {
	Log(LOG_INFO, ":init(%lu) : Initializing..", timeout );
	this->info.kernel = Object::getURI().getKernelName();
	if ( this->info.kernel.empty() ) // overwrite
		this->info.kernel = WoshKernel::getKernelName();
	this->info.primary = true;
	this->info.lastSeen = 0;
	this->agents.erase( this->info.id, false ); // it's a shadow reference: don't 'delete'
	this->agents.free();
	this->agents.set( this->info.id, &this->info);
	Object::getURI().registerGlobal();
	Properties.updateObjectKeys(this);
	if ( !BusCore.isConnected() ) {
		BusCore.setMessageHandler(this);
		WRESULT bus_c_connected = BusCore.connect( _Bus_Core, Bus::ACTION_DEFERRED );
		if ( WFAILED(bus_c_connected) )
			Log(LOG_WARNING, ":init() : FAILED#%d Connecting Bus ("_Bus_Core")..", bus_c_connected );
	 }
	if ( !BusDSM.isConnected() ) {
		if ( !WoshKernel::getInstance()->busses().existsBus(_Bus_DSM) ) {
			Log(LOG_INFO, ":init() : Initializing BusRing ("_Bus_DSM").." );
			Bus* dsmBus = new BusRing(_Bus_DSM);
			dsmBus->setParentObject(this);
			dsmBus->setSharedLocal(false);
			dsmBus->setSharedNetwork(true);
			WRESULT created_bus = WoshKernel::getInstance()->busses().registerBus(dsmBus);
			if ( WFAILED(created_bus) )
				Log(LOG_CRITICAL, ":init() : FAILED#%d Creating BusRing ("_Bus_DSM")", created_bus );
			else
				Log(LOG_INFO, ":init() : Created BusRing ("_Bus_DSM")." );
		 }
		BusDSM.setMessageHandler(this);
		WRESULT bus_d_connected = BusDSM.connect( _Bus_DSM, true ); // ACTION_DEFERRED | BUS_CREATE_IF_MISSING
		if ( WFAILED(bus_d_connected) ) {
			Log(LOG_CRITICAL, ":init() : FAILED#%d Connecting Bus ("_Bus_DSM")..", bus_d_connected );
			return WRET_ERR_INTERNAL;
		 }
	 }
	Log(LOG_DEBUG, ":init() : raise an Election (intrinsically asynch).." );
	this->agents.transactionBeginRead();
	doElectionRequest_(0);
	this->agents.transactionEnd();
	return WRET_OK;
 }

WRESULT SharedMemory::deinit( unsigned long timeout ) {
	Log(LOG_INFO, ":deinit(%lu) : DeInitializing..", timeout );
	Object::getURI().unRegisterGlobal();
	Log(LOG_DEBUG, ":deinit() : Announcing my Death.." );
	doAnnounceDeath();
	Log(LOG_DEBUG, ":deinit() : Disconnecting bus.." );
	// no need to wait for disconnection
	WRESULT bus_d_disconnected = BusDSM.disconnect(Bus::ACTION_DEFERRED);
	if ( WFAILED(bus_d_disconnected) )
		Log(LOG_CRITICAL, ":deinit() : FAILED#%d DisConnecting Bus ("_Bus_DSM")..", bus_d_disconnected );
	BusDSM.setMessageHandler(NULL);
	WRESULT bus_c_disconnected = WRET_OK;
	if ( BusCore.isConnected() ) {
		bus_c_disconnected = BusCore.disconnect(Bus::ACTION_DEFERRED);
		if ( WFAILED(bus_c_disconnected) )
			Log(LOG_CRITICAL, ":deinit() : FAILED#%d DisConnecting Bus ("_Bus_Core")..", bus_c_disconnected );
	 }
	BusCore.setMessageHandler(NULL);
	return bus_c_disconnected + bus_d_disconnected;
 }

//////////////////////////////////////////////////////////////////////////////////////////////// FLOW/CONTROL
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////// ELECTION

void SharedMemory::doElectionRequest() {
	MutexLockerRead(this->agents.getMutex());
	doElectionRequest_(0);
 }

void SharedMemory::doElectionRequest_( long target_agent ) {
	int64 newclock = ++this->info.clock; // increase clock
	// send decorated list of known agents including myself
	List* agent_list = NULL;
	//if ( send_list ) {
		agent_list = new List( this->agents.size() );
		agent_list->fromMapValue(this->agents.getMap(), true);
		Log(LOG_INFO, ":doElectionRequest() : Sending to %ld.. (++Clock=%"PRId64",Agents=%d)", target_agent, newclock, agent_list->size() );
	// }
	//else
	//	Log(LOG_INFO, ":doElectionRequest() : Sending to %ld.. (++Clock=%"PRId64")", target_agent, newclock );
	sendToAgent( new Message(new Request(_SharedMemory_METHOD_election, agent_list)), target_agent );
 }

void SharedMemory::doAnnounceDeath() {
	// remove myself from the agent list, fix its primary flag
	// broadcast an election-rensponse
	this->agents.transactionBeginWrite();
	int64 newclock = ++this->info.clock; // increase clock
	this->agents.erase( this->info.id, false ); // it's a shadow reference: don't 'delete'
	this->agents.clearNullValues(); // sence we are writing, let's clear empty slots
	long new_primary = fixPrimaryFlag_(); // fix the 'primary' flag evaluating seniority, returns new primary id
	List* agent_list = new List( this->agents.size() );
	agent_list->fromMapValue(this->agents.getMap(), true);
	this->agents.transactionEnd();
	Log(LOG_INFO, ":doAnnounceDeath() : Sending Response.. (++Clock=%"PRId64",Primary=%ld,Agents=%d)", newclock, new_primary, agent_list->size() );
	sendToAgent( new Message(new Response(_SharedMemory_METHOD_election, agent_list)), 0 );
 }

void SharedMemory::onElectionReceivedRequest( const List* request_agents, long sender_id, long target_id ) {
	// use-cases:
	//  - agent was initialized: sender_id=NEW_AGENT [primary=true,lastSeen=0], target_id=0
	//    then it can't be the primary (since i'm alive), so add it and (if primary) broadcast response 
	//  - agent has (primary/list) conflicts and wants to retrieve my 'official' list
	this->agents.transactionBeginWrite();
	int64 newclock = ++this->info.clock; // increase clock (always)
	// is it a new agent? (just initialized)
	bool new_agent_init = !this->agents.find(sender_id);
	if ( request_agents != NULL ) {
		int added = doElectionEvaluateAgentsNew_(*request_agents, sender_id, target_id, SM_AgentsUpdateFlag_UPDATE | SM_AgentsUpdateFlag_ADD_SENDER);
		long new_primary = fixPrimaryFlag_();
		if ( added )
			Log(LOG_INFO, ":onElectionReceivedRequest() : Updated & Added new agent from %ld.. (++Clock=%"PRId64",Primary=%ld,Agents=%d /%d)", sender_id, newclock, new_primary, this->agents.size(), request_agents->size() );
		else
			Log(LOG_VERBOSE, ":onElectionReceivedRequest() : Updated agents from %ld.. (++Clock=%"PRId64",Primary=%ld,Agents=%d /%d)", sender_id, newclock, new_primary, this->agents.size(), request_agents->size() );
	 }
	else if ( new_agent_init ) { // should never get here
		Log(LOG_WARNING, ":onElectionReceivedRequest() : Missing List from %ld, Sending request.. (++Clock=%"PRId64",Agents=%d)", sender_id, newclock, this->agents.size() );
		doElectionRequest_(sender_id);
		this->agents.transactionEnd();
		return;
	 }
	// if target is broadcast, only (real) primary replies, else only target replies
	if ( this->info.primary || target_id == this->info.id ) {
		List* agent_list = new List( this->agents.size() );
		agent_list->fromMapValue(this->agents.getMap(), true);
		this->agents.transactionEnd();
		if ( new_agent_init ) sender_id = 0; // force a broadcast response, so all agents are updated
		Log(LOG_INFO, ":onElectionReceivedRequest() : Sending Response to %ld.. (++Clock=%"PRId64",Agents=%d)", sender_id, newclock, agent_list->size() );
		sendToAgent( new Message(new Response(_SharedMemory_METHOD_election, agent_list)), sender_id );
	 } else {
		this->agents.transactionEnd();
	 }
 }

void SharedMemory::onElectionReceivedResponse( const List& response_agents, long sender_id, long target_id ) {
	if ( sender_id == 0 ) return; // error!? should never happen
	// use-cases:
	//  - broadcast response, is it (new) primary spreading latest update? => apply
	//  - requested response, i'm target since i did a request => apply
	//  - death notification, so remove it and broadcast response (if primary)
	this->agents.transactionBeginWrite();
	int64 newclock = ++this->info.clock; // increase clock (always)
	int conflicts = checkConflicts_(response_agents, sender_id);
	// eval conflicts in the incoming list (and versus known info)
	//  - MY clock skew => ignore (since i should already )
	//  - SENDER clock skew => ignore (since i should already )
	if ( (conflicts & SM_AgentsConflicts_CLOCK_IM_IN_FUTURE) == SM_AgentsConflicts_CLOCK_IM_IN_FUTURE ||
		(conflicts & SM_AgentsConflicts_CLOCK_SENDER_IN_PAST) == SM_AgentsConflicts_CLOCK_SENDER_IN_PAST ) {
		Log(LOG_WARNING, ":onElectionReceivedResponse() : IGNORED due Conflicts(#%d) from %ld (++Clock=%"PRId64",Agents=%d /%d)", conflicts, sender_id, newclock, this->agents.size(), response_agents.size() );
		this->agents.transactionEnd();
		return;
	 }
	// if it's a death notification, we always apply the update
	long new_primary = 0;
	bool death_notif = isDeathNotification(response_agents, sender_id, target_id);
	if ( death_notif ) {
		// then remove it from my list and update primary
		// if primary we will confirm it sending another broadcast response
		this->agents.erase(sender_id);
		new_primary = fixPrimaryFlag_();
		Log(LOG_INFO, ":onElectionReceivedResponse() : Removed death agent from %ld.. (++Clock=%"PRId64",Primary=%ld,Agents=%d /%d)", sender_id, newclock, new_primary, this->agents.size(), response_agents.size() );
	 }
	int added = doElectionEvaluateAgentsNew_(response_agents, sender_id, target_id, SM_AgentsUpdateFlag_UPDATE | SM_AgentsUpdateFlag_ADD_ALL);
	new_primary = fixPrimaryFlag_();
	if ( added )
		Log(LOG_INFO, ":onElectionReceivedResponse() : Updated & Added new agents(%d) from %ld.. (++Clock=%"PRId64",Primary=%ld,Agents=%d /%d)", added, sender_id, newclock, new_primary, this->agents.size(), response_agents.size() );
	else
		Log(LOG_VERBOSE, ":onElectionReceivedResponse() : Updated agents from %ld.. (++Clock=%"PRId64",Primary=%ld,Agents=%d /%d)", sender_id, newclock, new_primary, this->agents.size(), response_agents.size() );
	int removed = 0;
	if ( isPrimary_(sender_id) || target_id == this->info.id ) {
		int removed = doElectionEvaluateAgentsOld_(response_agents, sender_id, target_id);
		if ( removed ) {
			new_primary = fixPrimaryFlag_();
			Log(LOG_INFO, ":onElectionReceivedResponse() : Removed old agents(%d) from %ld.. (++Clock=%"PRId64",Primary=%ld,Agents=%d /%d)", removed, sender_id, newclock, new_primary, this->agents.size(), response_agents.size() );
		 }
	 }
	bool spread = death_notif | (bool)added | (bool)removed;
	if ( this->info.primary && spread ) {
		// if i'm primary & there were changes, broadcast another response
		List* agent_list = new List( this->agents.size() );
		agent_list->fromMapValue(this->agents.getMap(), true);
		this->agents.transactionEnd();
		Log(LOG_INFO, ":onElectionReceivedResponse() : Confirming with Response.. (++Clock=%"PRId64",Agents=%d)", newclock, agent_list->size() );
		sendToAgent( new Message(new Response(_SharedMemory_METHOD_election, agent_list)), 0 );
	 }
	else if ( new_primary != sender_id ) {
		// if another agent is primary, reply request (to ensure)
		Log(LOG_WARNING, ":onElectionReceivedResponse() : Updated from !primary(%ld), Sending request to %ld.. (++Clock=%"PRId64",Agents=%d /%d)", sender_id, new_primary, newclock, this->agents.size(), response_agents.size() );
		doElectionRequest_(new_primary);
		this->agents.transactionEnd();
	 }
	else {
		this->agents.transactionEnd();
	 }
 }

// --------------------------------------------------------------------------------------------------

int SharedMemory::doElectionEvaluateAgentsNew_( const List& response_agents, long sender_id, long target_id, int update_flags ) {
	int newAgents = 0;
	//add/update agent when (clock > current_clock), ignore else
	List::ConstIterator it;
	const List::ConstIterator it_end = response_agents.end();
	for( it=response_agents.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		const SharedMemoryInfo* ainfo = (*it)->as<SharedMemoryInfo>();
		if ( ainfo == NULL ) continue;
		if ( ainfo->id == this->info.id ) {
			this->info.lastSeen = Utilities::std_time();
			continue;
		 }
		if ( ainfo->strategy != this->info.strategy ) {
			Log(LOG_WARNING, ":doElectionEvaluateAgentsNew_() : Disaligned strategy for agent#%ld (%d)", ainfo->id, ainfo->strategy );
			continue;
		 }
		SharedMemoryInfo* local_ainfo = this->agents.find(ainfo->id);
		if ( local_ainfo == NULL ) {
			if ( ainfo->id == sender_id ) {
				if ( (update_flags & SM_AgentsUpdateFlag_ADD_SENDER) == 0 ) continue;
			 } else {
				if ( (update_flags & SM_AgentsUpdateFlag_ADD_BUT_SENDER) == 0 ) continue;
			 }
			bool allowed = onAgentAdding( *local_ainfo, sender_id, target_id );
			if ( !allowed ) {
				Log(LOG_WARNING, ":doElectionEvaluateAgentsNew_() : Skipped Adding agent#%ld", ainfo->id );
				continue;
			 }
			Log(LOG_VERBOSE, ":doElectionEvaluateAgentsNew_() : Added agent#%ld", ainfo->id );
			SharedMemoryInfo* ainfo_clone = ainfo->clone();
			ainfo_clone->lastSeen = Utilities::std_time();
			this->agents.set(ainfo->id, ainfo_clone);
			++newAgents;
		 }
		else if ( ainfo->clock >= local_ainfo->clock ) {
			if ( (update_flags & SM_AgentsUpdateFlag_UPDATE) == 0 ) continue;
			bool allowed = onAgentUpdating( *local_ainfo, sender_id, target_id );
			if ( !allowed ) {
				Log(LOG_WARNING, ":doElectionEvaluateAgentsNew_() : Skipped Updating agent#%ld", ainfo->id );
				continue;
			 }
			Log(LOG_DEBUG, ":doElectionEvaluateAgentsNew_() : Updated agent#%ld [clock: old=%"PRId64",new=%"PRId64"]", ainfo->id, local_ainfo->clock, ainfo->clock );
			*local_ainfo = *ainfo;
			local_ainfo->lastSeen = Utilities::std_time();
		 }
		else
			Log(LOG_DEBUG, ":doElectionEvaluateAgentsNew_() : Ignored agent#%ld [clock: old=%"PRId64",new=%"PRId64"]", ainfo->id, local_ainfo->clock, ainfo->clock );
	 }
	return newAgents;
 }

int SharedMemory::doElectionEvaluateAgentsOld_( const List& response_agents, long sender_id, long target_id ) {
	// clear previous nulls 
	this->agents.clearNullValues();
	// delete agents only when: primary is aligned and is the sender -OR- on death notifications
	List::ConstIterator it;
	const List::ConstIterator it_end = response_agents.end();
	tLongSharedMemoryInfoMap::Iterator ita;
	const tLongSharedMemoryInfoMap::Iterator ita_end = this->agents.end();
	// eval if it is a death notification (=list without sender entry)

	bool found = false;
	for( ita=this->agents.begin(); ita!=ita_end; ++ita ) {
		if ( ita->second == NULL ) continue;
		if ( ita->second == &this->info ) continue;
		found = false;
		for( it=response_agents.begin(); it!=it_end; ++it ) {
			if ( *it == NULL ) continue;
			const SharedMemoryInfo* ainfo = (*it)->as<SharedMemoryInfo>();
			if ( ainfo == NULL ) continue;
			if ( ainfo->id != ita->second->id ) continue;
			found = true;
			break;
		 }
		if ( !found ) {
			bool allowed = onAgentRemoving( *ita->second, sender_id, target_id );
			if ( !allowed ) {
				Log(LOG_WARNING, ":doElectionEvaluateAgentsOld_() : Skipped Removing agent#%ld [last_clock=%"PRId64"]", ita->second->id, ita->second->clock );
				continue;
			 }
			Log(LOG_DEBUG, ":doElectionEvaluateAgentsOld_() : Removed agent#%ld [last_clock=%"PRId64"]", ita->second->id, ita->second->clock );
			delete ita->second; ita->second = NULL;
		 }
	 }
	int removed = this->agents.clearNullValues();
	if ( removed > 0 ) Log(LOG_VERBOSE, ":doElectionEvaluateAgentsOld_() : Removed %d agents", removed );
	return removed;
 }

// --------------------------------------------------------------------------------------------------

void SharedMemory::onWoshHostConnectionChanged( const std::string& hostname, int connection_state, const Message* ) {
	// a new WOSH host has been (dis)connected, it may host agents (even primary)
	if ( connection_state == _WoshHost_Connected ) {
		// a new WOSH host has been connected (1 hop), it may host agents.
		if ( this->info.primary ) {
			// since i'm primary, send an election response to such host
			this->agents.transactionBeginRead();
			int64 newclock = ++this->info.clock; // increase clock
			Log(LOG_INFO, ":onWoshHostConnectionChanged(%s,[Connected]) : Sending Election-Response.. (++Clock=%"PRId64",Primary,LAgents=%d)", hostname.c_str(), newclock, this->agents.size() );
			List* agent_list = new List( this->agents.size() );
			agent_list->fromMapValue(this->agents.getMap(), true);
			this->agents.transactionEnd();
			Message* message = new Message(new Request(_SharedMemory_METHOD_election, agent_list));
			message->setDestinationBus(_Bus_DSM);
			message->setDestination(this->getURI());
			message->getDestination().setKernelName(hostname);
			message->getDestination().setNameAny();
			message->setSource(this);
			SecurityManager::getCredentialImplicit().signMessage(message, this);
			BusDSM.postMessage(message);
		 }
		else
			Log(LOG_DEBUG, ":onWoshHostConnectionChanged(%s,[Connected]) : Ignored (since not primary)", hostname.c_str() );
		return;
	 }
	else if ( connection_state == _WoshHost_Disconnected ) {
		// a connected WOSH host has been disconnected (1 hop), it may host agents (even primary).
		this->agents.transactionBeginRead();
		this->agents.clearNullValues(); // clear nulls before loop, so we will count removed#
		long my_primary = findPrimary_();
		tLongSharedMemoryInfoMap::Iterator ita; const tLongSharedMemoryInfoMap::Iterator ita_end = this->agents.end();
		for( ita=this->agents.begin(); ita!=ita_end; ++ita ) {
			if ( ita->second == NULL ) continue;
			if ( ita->second->id == this->info.id ) continue;
			if ( ita->second->kernel != hostname ) continue;
			// found an agent on disconnected host..
			bool allowed = onAgentRemoving( *ita->second, 0, 0 );
			if ( !allowed ) {
				Log(LOG_WARNING, ":onWoshHostConnectionChanged(%s,[Disconnected]) : Skipped Removing Agent %ld", hostname.c_str(), ita->second->id );
				continue;
			 }
			if ( ita->second->id == my_primary )
				Log(LOG_DEBUG, ":onWoshHostConnectionChanged(%s,[Disconnected]) : Removed Primary Agent %ld", hostname.c_str(), ita->second->id );
			else
				Log(LOG_DEBUG, ":onWoshHostConnectionChanged(%s,[Disconnected]) : Removed Agent %ld", hostname.c_str(), ita->second->id );
			delete ita->second; ita->second = NULL;
		 }
		int removed = this->agents.clearNullValues(); // number of hosts have been just removed 
		if ( removed > 0 ) {
			int64 newclock = ++this->info.clock; // increase clock
			long my_new_primary = findPrimary_();
			my_primary = findPrimary_(); // is one flagged as primary (or did we just remove it => 0)
			if ( my_primary == 0 ) {
				Log(LOG_INFO, ":onWoshHostConnectionChanged(%s,[Disconnected]) : Updated removed Primary (%ld=>%ld). (++Clock=%"PRId64",LAgents=%d)", hostname.c_str(), my_primary, my_new_primary, newclock, this->agents.size() );
			 }
			else if ( my_new_primary != my_primary )
				Log(LOG_INFO, ":onWoshHostConnectionChanged(%s,[Disconnected]) : Updated disaligned Primary (%ld=>%ld). (++Clock=%"PRId64",LAgents=%d)", hostname.c_str(), my_primary, my_new_primary, newclock, this->agents.size() );
			fixPrimaryFlag_(my_new_primary);
			if ( this->info.primary && this->agents.size() > 1 ) {
				// since primary, spread the update
				List* agent_list = new List( this->agents.size() );
				agent_list->fromMapValue(this->agents.getMap(), true);
				Log(LOG_INFO, ":onWoshHostConnectionChanged(%s,[Disconnected]) : Removed %d agents, (since Primary) Broadcasting Election-Response.. (++Clock=%"PRId64",Agents=%d)", hostname.c_str(), removed, newclock, agent_list->size() );
				sendToAgent( new Message(new Response(_SharedMemory_METHOD_election, agent_list)), 0 );
			 }
			else
				Log(LOG_INFO, ":onWoshHostConnectionChanged(%s,[Disconnected]) : Removed %d agents (++Clock=%"PRId64",Primary=%ld,LAgents=%d)", hostname.c_str(), removed, newclock, my_primary, this->agents.size() );
		 }
		else
			Log(LOG_DEBUG, ":onWoshHostConnectionChanged(%s,[Disconnected]) : Ignored (since no changes)", hostname.c_str() );
		this->agents.transactionEnd();
	 }
 }

int SharedMemory::checkConflicts_( const List& container, long sender_id ) const {
	// detect conflicts:
	int conflicts = SM_AgentsConflicts_VALID;
	long oldest = 0; double seniority = (double)LIMIT_LONG_MAX;
	long first_primary = 0;
	List::ConstIterator it;	const List::ConstIterator it_end = container.end();
	for( it=container.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		const SharedMemoryInfo* ainfo = (*it)->as<SharedMemoryInfo>();
		if ( ainfo == NULL ) continue;
		if ( ainfo->seniority < seniority ) {
			// eval: REAL primary, find the oldest (smaller) seniority
			oldest = ainfo->id;
			seniority = ainfo->seniority;
		 }
		if ( ainfo->primary ) {
			// eval: multiple primary flagged
			if ( first_primary == 0 )
				first_primary = ainfo->id;
			else {
				conflicts |= SM_AgentsConflicts_PRIMARY_MULTIPLE;
				Log(LOG_VERBOSE, ":checkConflicts_() : Multiple Primary flag {%ld,?,%ld} (Sender=%ld,Agents=%d)", first_primary, ainfo->id, sender_id, container.size() );
			 }
		 }
		if ( ainfo->id == this->info.id ) {
			// eval: claimed (myself) clock > local clock
			if ( ainfo->clock > this->info.clock ) {
				conflicts |= SM_AgentsConflicts_CLOCK_IM_IN_FUTURE;
				Log(LOG_VERBOSE, ":checkConflicts_() : My remote clock is in future {%"PRId64">%"PRId64"} (Sender=%ld,Agents=%d)", ainfo->clock, this->info.clock, sender_id, container.size() );
			 }
		 }
		if ( sender_id != 0 && ainfo->id == sender_id ) {
			// eval: claimed (sender) clock < local (sender) clock
			const SharedMemoryInfo* ainfo_local = this->agents.find(sender_id);
			if ( ainfo_local != NULL && ainfo->clock < ainfo_local->clock ) {
				conflicts |= SM_AgentsConflicts_CLOCK_SENDER_IN_PAST;
				Log(LOG_VERBOSE, ":checkConflicts_() : Sender clock is in past {%"PRId64">%"PRId64"} (Sender=%ld,Agents=%d)", ainfo->clock, ainfo_local->clock, sender_id, container.size() );
			 }
		 }
	 }
	if ( oldest != first_primary ) {
		// eval: primary is not the oldest
		conflicts |= SM_AgentsConflicts_PRIMARY_NOT_OLDEST;
		Log(LOG_VERBOSE, ":checkConflicts_() : Disaligned Primary flag {%ld vs. %ld} (Sender=%ld,Agents=%d)", oldest, first_primary, sender_id, container.size() );
	 }
	return conflicts;
 }

// --------------------------------------------------------------------------------------------------

bool SharedMemory::isPrimary_( long sender_id ) const {
	const SharedMemoryInfo* ainfo = this->agents.find(sender_id);
	if ( ainfo == NULL ) return false;
	return ainfo->primary;
 }

bool SharedMemory::isDeathNotification( const List& agents, long sender_id, long target_id ) const {
	if ( sender_id == 0 || target_id != 0 ) return false;
	const List::ConstIterator it_end = agents.end();
	for( List::ConstIterator it=agents.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		const SharedMemoryInfo* ainfo = (*it)->as<SharedMemoryInfo>();
		if ( ainfo == NULL || ainfo->id != sender_id ) continue;
		return false;
	 }
	return true;
 }
 
bool SharedMemory::isNewAgent( const List& agents, long sender_id, long target_id ) const {
	if ( sender_id == 0 || target_id != 0 ) return false;
	const List::ConstIterator it_end = agents.end();
	for( List::ConstIterator it=agents.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		const SharedMemoryInfo* ainfo = (*it)->as<SharedMemoryInfo>();
		if ( ainfo == NULL || ainfo->id != sender_id ) continue;
		return true;
	 }
	return false;
 }

long SharedMemory::findPrimary_() const {
	long oldest = 0; double seniority = (double)LIMIT_LONG_MAX;
	tLongSharedMemoryInfoMap::ConstIterator ita; const tLongSharedMemoryInfoMap::ConstIterator ita_end = this->agents.end();
	for( ita=this->agents.begin(); ita!=ita_end; ++ita ) {
		if ( ita->second == NULL ) continue;
		if ( ita->second->seniority > seniority ) continue;
		oldest = ita->second->id;
		seniority = ita->second->seniority;
	 }
	return oldest;
 }

long SharedMemory::findPrimary( const List& container ) const {
	long oldest = 0; double seniority = (double)LIMIT_LONG_MAX;
	List::ConstIterator it; const List::ConstIterator it_end = container.end();
	for( it=container.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		const SharedMemoryInfo* ainfo = (*it)->as<SharedMemoryInfo>();
		if ( ainfo == NULL ) continue;
		if ( ainfo->seniority > seniority ) continue;
		oldest = ainfo->id;
		seniority = ainfo->seniority;
	 }
	return oldest;
 }

long SharedMemory::fixPrimaryFlag_( long new_primary ) {
	if ( new_primary == 0 ) new_primary = findPrimary_();
	tLongSharedMemoryInfoMap::ConstIterator ita; const tLongSharedMemoryInfoMap::ConstIterator ita_end = this->agents.end();
	for( ita=this->agents.begin(); ita!=ita_end; ++ita ) {
		if ( ita->second == NULL ) continue;
		ita->second->primary = (ita->second->id == new_primary);
	 }
	return new_primary;
 }

long SharedMemory::fixPrimaryFlag( List& container, long new_primary ) const {
	if ( new_primary == 0 ) new_primary = findPrimary(container);
	List::Iterator it; const List::Iterator it_end = container.end();
	for( it=container.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		SharedMemoryInfo* ainfo = (*it)->as<SharedMemoryInfo>();
		if ( ainfo == NULL ) continue;
		ainfo->primary = (ainfo->id == new_primary);
	 }
	return new_primary;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////// ELECTION
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////// ELECTION EVENTS

bool SharedMemory::onAgentAdding( const SharedMemoryInfo& , long , long ) {
	return true;
 }

bool SharedMemory::onAgentUpdating( const SharedMemoryInfo& , long , long ) {
	return true;
 }

bool SharedMemory::onAgentRemoving( const SharedMemoryInfo& , long , long ) {
	return true;
 }

bool SharedMemory::onAgentPrimaryChanged( const SharedMemoryInfo& , long , long ) {
	return true;
 }

///////////////////////////////////////////////////////////////////////////////////////////// ELECTION EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// FULL SYNCH

List* SharedMemory::copyDataSet() const {
	this->dataset.transactionBeginRead();
	List* ds_list = new List( this->dataset.size() );
	tStrSharedObjectMap::ConstIterator it;
	tStrSharedObjectMap::ConstIterator it_end = this->dataset.end();
	for ( it=this->dataset.end(); it!=it_end; ++it ) {
		if ( it->second == NULL ) ds_list->push_back(NULL);
		else ds_list->push_back( it->second->clone() );
	 }
	this->dataset.transactionEnd();
	return ds_list;
 }

void SharedMemory::onDataSetReceived( const List& response_dataset, const Message* ) {
	Log(LOG_INFO, ":onDataSetReceived() : Merging %d items ", response_dataset.size() );
	mergeDataSet(response_dataset);
 }

int SharedMemory::mergeDataSet( const List& response_dataset ) {

	return 0;
 }

////////////////////////////////////////////////////////////////////////////////////////////////// FULL SYNCH
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// BUS EVENTS

void SharedMemory::busMessage( const Message& message, const Bus* source ) {
	if ( message.isEmpty() ) return;
	if ( message.getCurrentBus() == _Bus_Core ) {
		busMessage_Core(message, source);
		return;
	 }
	if ( !MessageFilter::isMessageTarget(message, Object::getURI() ) ) return;
	if ( message.getSourceUUID() == this->getEntityID() ) return; 
	long sender_id = 0;	long target_id = 0;
	if ( !message.getSource().isNameAny() ) sender_id = Utilities::fromString<long>(message.getSource().getName());
	if ( !message.getDestination().isNameAny() ) target_id = Utilities::fromString<long>(message.getDestination().getName());
	if ( message.getContent()->isRequest() ) {
		const Request* request = message.getContent()->asRequest();
		if ( request == NULL ) return;
		if ( request->getMethod() == _SharedMemory_METHOD_election ) {
			const List* agents = NULL;
			if ( request->hasData() && request->getData()->isKindOf<List>())
				agents = request->getData()->as<List>();
			onElectionReceivedRequest( agents, sender_id, target_id);
		 }
		else if ( request->getMethod() == _SharedMemory_METHOD_get_dataset ) {
			List* ds = copyDataSet();
			Message* reply = message.replyTo( request->replyResponse(WRET_OK, ds) );
			reply->setSource(this);
			SecurityManager::getCredentialImplicit().signMessage(reply, this);
			BusDSM.postMessage(reply);
		 }
	 }
	else if ( message.getContent()->isResponse() ) {
		const Response* response = message.getContent()->asResponse();
		if ( response == NULL ) return;
		if ( response->getMethod() == _SharedMemory_METHOD_election ) {
			if ( !response->hasData() || !response->getData()->isKindOf<List>()) return;
			const List* agent_list = response->getData()->as<List>();
			if ( agent_list == NULL ) return;
			onElectionReceivedResponse( *agent_list, sender_id, target_id);
		 }
		else if ( response->getMethod() == _SharedMemory_METHOD_get_dataset ) {
			if ( !response->hasData() || !response->getData()->isKindOf<List>()) return;
			const List* ds_list = response->getData()->as<List>();
			if ( ds_list == NULL ) return;
			onDataSetReceived( *ds_list, &message );
		 }
	 }
 }

void SharedMemory::busMessage_Core( const Message& message, const Bus* source ) {
	if ( message.isEmpty() ) return;
	if ( !MessageFilter::isMessageTarget(message, Object::getURI() ) ) return;
	if ( message.getSourceUUID() == this->getEntityID() ) return; 
	if ( message.getContent()->isFact() && message.getContent()->asFact()->getEvent() == _WoshHost_EVENT_ConnectionChanged ) {
		const WoshHost_ConnectionChanged* wcc = message.getContent()->as<WoshHost_ConnectionChanged>();
		if ( wcc == NULL ) return;
		onWoshHostConnectionChanged( wcc->getWoshHostName(), wcc->getConnectionState(), &message );
	 }
	// standard WOSH request, mostly debug stuff (not from DSMs)
	else if ( message.getContent()->isRequest() ) {
		const Request* request = message.getContent()->asRequest();
/*
		if ( request->getMethod() == _SharedMemory_METHOD_raise_election ) {
			doElectionRequest();
			Message* reply = message.replyTo( request->replyResponse(WRET_OK) );
			reply->setSource(this);
			SecurityManager::getCredentialImplicit().signMessage(reply, this);
			BusCore.postMessage(reply);
		 }
		else if ( request->getMethod() == _SharedMemory_METHOD_get_agents ) {
			this->agents.transactionBeginRead();
			List* agent_list = new List( this->agents.size() );
			agent_list->fromMapValue(this->agents.getMap(), true);
			Message* reply = message.replyTo( request->replyResponse(WRET_OK, agent_list) );
			this->agents.transactionEnd();
			reply->setSource(this);
			SecurityManager::getCredentialImplicit().signMessage(reply, this);
			BusCore.postMessage(reply);
		 }
		else if ( request->getMethod() == _SharedMemory_METHOD_get_info ) {
			this->agents.transactionBeginRead();
			Message* reply = message.replyTo( request->replyResponse(WRET_OK, this->info.clone()) );
			this->agents.transactionEnd();
			reply->setSource(this);
			SecurityManager::getCredentialImplicit().signMessage(reply, this);
			BusCore.postMessage(reply);
		 }
		else 
*/
		if ( request->getMethod() == _SharedMemory_METHOD_get_dataset ) {
			List* ds = copyDataSet();
			if ( request->hasData() ) {
				Variant target(0);
				request->extractArguments(target);
				sendToAgent( new Message(request->replyResponse(WRET_OK, ds)), target.toLong(0) );
			 }
			else {
				Message* reply = message.replyTo( request->replyResponse(WRET_OK, ds) );
				reply->setSource(this);
				SecurityManager::getCredentialImplicit().signMessage(reply, this);
				BusCore.postMessage(reply);
			 }
		 }
		else {
			Message* reply = MPC_busMessageRequest( message, this->Methods, this->Log );
			if ( reply == NULL ) return;
			if ( !reply->hasSecurityToken() )
				SecurityManager::getCredentialImplicit().signMessage(reply, this);
			if ( source != NULL )
				source->postMessage(reply);
			else
				BusCore.postMessage(reply);
		 }
	 }
 }

////////////////////////////////////////////////////////////////////////////////////////////////// BUS EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENTS

bool SharedMemory::readingProperty( Property* property_curr, const PropertiesProvider* ) {
	if ( property_curr == NULL ) return true;
	if ( property_curr->getKey() == _SharedMemory_KEY_StatsAgentsCount ) {
		this->agents.transactionBeginRead();
		property_curr->getValue().set( this->agents.size() );
		this->agents.transactionEnd();
	 }
	else if ( property_curr->getKey() == _SharedMemory_KEY_StatsDataSetCount ) {
		this->dataset.transactionBeginRead();
		property_curr->getValue().set( this->dataset.size() );
		this->dataset.transactionEnd();
	 }
	else if ( property_curr->getKey() == _SharedMemory_KEY_Clock ) {
		this->dataset.transactionBeginRead();
		property_curr->getValue().set( getClock() );
		this->dataset.transactionEnd();
	 }
	else if ( property_curr->getKey() == _SharedMemory_KEY_Primary ) {
		this->dataset.transactionBeginRead();
		property_curr->getValue().set( isPrimary() );
		this->dataset.transactionEnd();
	 }
	else if ( property_curr->getKey() == _SharedMemory_KEY_StatsDataSetCount ) {
		this->dataset.transactionBeginRead();
		property_curr->getValue().set( this->dataset.size() );
		this->dataset.transactionEnd();
	 }
	return true;
 }

bool SharedMemory::updatingProperty( bool& do_update, const Variant& value_proposed, Property& property_current, const PropertiesProvider* ) {
	if ( property_current.getKey() == _KEY_DebugLevel ) {
		return MPC_PropertyParse_LogLevel( value_proposed, this->Log );
	 }
	else if ( property_current.getKey() == _SharedMemory_KEY_Mode ) {
		setMode( static_cast<SharedMemory::AGENT_MODE>(value_proposed.toInteger(static_cast<int>(getMode()))) );
	 }
	else if ( property_current.getKey() == _SharedMemory_KEY_Propagation ) {
		setPropagation( static_cast<SharedMemory::UPDATE_PROPAGATION>(value_proposed.toInteger(static_cast<int>(getPropagation()))) );
	 }
	else if ( property_current.getKey() == _SharedMemory_KEY_PeriodAutoElection ) {
		setElectionPeriodic( value_proposed.toLong(static_cast<long>(getElectionPeriod())) );
	 }
	else if ( property_current.getKey() == _SharedMemory_KEY_PeriodAutoDataSynch ) {
		setDataSynchPeriodic( value_proposed.toLong(static_cast<long>(getDataSynchPeriod())) );
	 }
	else
		return false;
	return true;
 }

///////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void SharedMemory::sendToAgent( Message* message, long target_id ) {
	if ( message == NULL ) return;
	message->setDestinationBus(_Bus_DSM);
	message->setDestination(this->getURI());
	message->getDestination().setKernelAny();
	if ( target_id == 0 )
		message->getDestination().setNameAny();
	else
		message->getDestination().setName( Utilities::toString(target_id) );
	message->setSource(this);
	SecurityManager::getCredentialImplicit().signMessage(message, this);
	BusDSM.postMessage(message);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

long SharedMemory::selectRandomAgent_() {
	if ( this->agents.size() <= 1 ) return 0; // only myself!?
	int rnd = Utilities::randomInt( 0, this->agents.size() );
	return this->agents.keyAt(++rnd);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void SharedMemory::setName( const std::string& name ) { 
	Object::getURI().clear();
	Object::getURI().setKernelLocal();
	string nname = name;
	if ( name.empty() ) {
		int name_id = Utilities::randomInt();
		name_id = _GET_Abs(name_id);
		nname = Utilities::format(_SharedMemory_DEFAULT_NAME"^%d", name_id );
		Log(LOG_DEBUG, ":setName() Empty name, defaulting to '%s'", nname.c_str() );
	 }
	Object::setName( nname, this->info.id );
	Object::getURI().getPath().push_back( name );
	Object::getURI().setName( Utilities::toString(this->info.id) );
	Log(LOG_INFO, ":setName(%s) Id: %ld, URI: %s", Object::getName().c_str(), this->info.id, Object::getURI().toString().c_str() );
	Log.setContext( "SM_" + Object::getName() );
 }

WRESULT SharedMemory::setElectionPeriodic( unsigned long period_seconds, unsigned long ctrl_timeout ) {
	if ( this->methodPeriodicElection == NULL ) {
		Log(LOG_VERBOSE, ":setElectionPeriodic(%lu,%lu) Creating ThreadMethodPeriodicT for SharedMemory::doElectionPeriodicUpdate..", period_seconds, ctrl_timeout );
		this->methodPeriodicElection = new ThreadMethodPeriodicT<SharedMemory, void>(this,"SharedMemory::doElectionPeriodicUpdate");
		this->methodPeriodicElection->setMethod(this, &SharedMemory::doElectionPeriodicUpdate );
	 }
	this->methodPeriodicElection->setPeriod(period_seconds);
	WRESULT ret = this->methodPeriodicElection->startThread(ctrl_timeout);
	if ( WFAILED(ret) ) {
		Log(LOG_CRITICAL, ":setElectionPeriodic(%lu,%lu) FAILED#%d stopping thread", period_seconds, ctrl_timeout, ret );
		Properties.transactionBeginWrite();
		Properties.update(_SharedMemory_KEY_PeriodAutoElection, Variant_TS(period_seconds*1000) );
		Properties.transactionEnd();
	 }
	else {
		Log(LOG_INFO, ":setElectionPeriodic(%lu,%lu) : Binded and thread started", period_seconds, ctrl_timeout );
		Properties.transactionBeginWrite();
		Properties.update(_SharedMemory_KEY_PeriodAutoElection, Variant_TS(0) );
		Properties.transactionEnd();
	 }
	return ret;
 }

WRESULT SharedMemory::setDataSynchPeriodic( unsigned long period_seconds, unsigned long ctrl_timeout ) {
	if ( this->methodPeriodicDataSynch == NULL ) {
		Log(LOG_VERBOSE, ":setDataSynchPeriodic(%lu,%lu) Creating ThreadMethodPeriodicT for SharedMemory::doDataSynchPeriodicUpdate..", period_seconds, ctrl_timeout );
		this->methodPeriodicDataSynch = new ThreadMethodPeriodicT<SharedMemory, void>(this,"SharedMemory::doDataSynchPeriodicUpdate");
		this->methodPeriodicDataSynch->setMethod(this, &SharedMemory::doDataSynchPeriodicUpdate );
	 }
	this->methodPeriodicDataSynch->setPeriod(period_seconds);
	WRESULT ret = this->methodPeriodicDataSynch->startThread(ctrl_timeout);
	if ( WFAILED(ret) ) {
		Log(LOG_CRITICAL, ":setDataSynchPeriodic(%lu,%lu) FAILED#%d stopping thread", period_seconds, ctrl_timeout, ret );
		Properties.transactionBeginWrite();
		Properties.update(_SharedMemory_KEY_PeriodAutoDataSynch, Variant_TS(period_seconds*1000) );
		Properties.transactionEnd();
	 }
	else {
		Log(LOG_INFO, ":setDataSynchPeriodic(%lu,%lu) : Binded and thread started", period_seconds, ctrl_timeout );
		Properties.transactionBeginWrite();
		Properties.update(_SharedMemory_KEY_PeriodAutoDataSynch, Variant_TS(0) );
		Properties.transactionEnd();
	 }
	return ret;
 }

long SharedMemory::getSeniorityTime() const {
	double fractpart, intpart;
	fractpart = modf (this->info.seniority , &intpart);
	return (long)intpart;
 }
long SharedMemory::getSeniorityReal() const {
	double fractpart, intpart;
	fractpart = modf (this->info.seniority , &intpart);
	return (long)fractpart;
 }
unsigned long SharedMemory::getElectionPeriod() const {
	if ( this->methodPeriodicElection == NULL ) return 0;
	return this->methodPeriodicElection->getPeriod();
 }
unsigned long SharedMemory::getDataSynchPeriod() const {
	if ( this->methodPeriodicDataSynch == NULL ) return 0;
	return this->methodPeriodicDataSynch->getPeriod();
 }

DataFieldCollector<SharedMemory>* SharedMemory::getDataFields() {
	DataFieldCollector<SharedMemory>* fields = new DataFieldCollector<SharedMemory>();
	fields->add<const std::string&, Variant>(&SharedMemory::getName, "Name", 0);
	fields->add<long,Variant>(&SharedMemory::getID, "ID", 3);
	fields->add<int64,Variant>(&SharedMemory::getClock, "Clock", 4);
	fields->add<double,Variant>(&SharedMemory::getSeniority, "Seniority", 5);
	fields->add<const char*,Variant>(&SharedMemory::getClassName, "Type", 6, false);
	return fields;
 }

const char* SharedMemory::getAgentModeAsString( AGENT_MODE mode ) {
	switch(mode) {
		case MODE_CLIENT:				return "CLIENT";
		case MODE_SERVER:				return "SERVER";
		case MODE_ADHOC:				return "ADHOC";
		case MODE_UNKNOWN:
		default :						return "UNKNOWN";
	 }
 }
const char* SharedMemory::getAgentStrategyAsString( AGENT_STRATEGY strategy ) {
	switch(strategy) {
		case STRATEGY_EPIDEMIC:			return "EPIDEMIC";
		case STRATEGY_DSM:				return "DSM";
		case STRATEGY_UNKNOWN:
		default :						return "UNKNOWN";
	 }
 }
const char* SharedMemory::getUpdatePropagationAsString( UPDATE_PROPAGATION propagation ) {
	switch(propagation) {
		case PROPAGATION_INVALIDATE:	return "INVALIDATE";
		case PROPAGATION_REPLICA:		return "REPLICA";
		case PROPAGATION_ACTIVE:		return "ACTIVE";
		case PROPAGATION_UNKNOWN:
		default :						return "UNKNOWN";
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::SharedMemory_ObjectInvalidation, "wosh::Fact", 1.0, _static_SharedMemory_ObjectInvalidation )

SharedMemory_ObjectInvalidation::SharedMemory_ObjectInvalidation( const std::string& obj_id )
	: Fact(_SharedMemory_EVENT_ObjectInvalidation) {
	setData( new Variant( obj_id ) );
 }
SharedMemory_ObjectInvalidation::SharedMemory_ObjectInvalidation( const std::string& obj_id, SharedObject::OPERATION operation )
	: Fact(_SharedMemory_EVENT_ObjectInvalidation) {
	List* list = new List(2);
	list->push_back<Variant>( obj_id );
	list->push_back<Variant>( (long)operation );
	setData(list);
 }

std::string SharedMemory_ObjectInvalidation::getObjectID() const {
	const Variant* var = getDataItem<Variant>(0);
	if ( var == NULL ) return "";
	if ( var->isString() ) return var->asString();
	return var->toString();
 }

SharedObject::OPERATION SharedMemory_ObjectInvalidation::getUpdateOperation() const {
	const Variant* var = getDataItem<Variant>(1);
	if ( var == NULL ) return SharedObject::OBJ_UNKNOWN;
	if ( var->isNumeric() ) return SharedObject::OBJ_UNKNOWN;
	return (SharedObject::OPERATION)var->toLong( (long)SharedObject::OBJ_UNKNOWN );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
